#include "ArmyState.h"
#include "UserAPI.h"
#include "BasicAI.h"
#include "params.h"

using namespace ArmyStateNS;

void DefensiveBattleState::execute()
{
	/*
		If target city becomes safe or it is already lost, then withdraw
		otherwise around_enemy < around_defender * 0.8, go to fuck them
		otherwise if I am in city region
			if I am at city center
				if I can recruit, go ahead
				if they have rangers, 
					if their ranger_count > my ranger_count or city.HP == 0, withdraw
					otherwise, if I can attack ranger, then attack ranger, 
					otherwise, if I can attack, then attack, 
					otherwise, if I can repare, then repare
				otherwise 
					if city.HP == 0 and somebody gets in, withdraw, 
					otherwise, if city.HP > 0 && I can attack, then attack, 
					otherwise, try to repare
			otherwise, I am not at the city center
				if nobody locates at city center, then tries to get to the city center, 
				otherwise, try to find a safe place outside the city to see what's going on.
		otherwise, I am outside the city region
			if I see extremely strong enemy, then withdraw,
			otherwise, try to approach ASAP.
	 */
	BasicAI *ai = BasicAI::instance();
	UserAPI &api = ai->get_api();
	Army *army = api.GetCntArmy();
	LandCity *target_city = api.GetCity(m_target);
	LOG_ASSERT(target_city, "target_city should not be NULL!");
	
	if (!api.InDanger(m_target)		//	 safe now
		|| target_city->owner != api.get_my_id() && api.GetMyArmyAround(m_target, 0).size() > 0	//	already lost
		)
	{
		enter_state(m_name, this, new WithdrawState(m_name));
		return;
	}

	vector<Army*> op_troop = api.GetOpArmyAround(m_target);
	vector<Army*> my_troop = api.GetMyArmyAround(m_target);

	int op_army_count = api.GetArmyCount(op_troop);
	int my_army_count = api.GetArmyCount(my_troop);

	if (op_army_count <= my_army_count * 0.8)
	{
		//	go to fuck them
		LOG << "Weak enemy, go to fuck them, op_count = " << op_army_count << ", my_count = " << my_army_count << endl;
		Army *nearest = api.GetNearestArmy(army->pos, op_troop);
		LOG_ASSERT(nearest, "nearest should not be NULL");
		enter_state(m_name, this, new TemporalAttackingState(m_name, this, nearest->general->type));
		return;
	}

	if (api.HamDistance(army->pos, api.GetCityCenter(target_city)) <= 9)
	{
		if (!(api.GetCityCenter(target_city) == army->pos))
		{
			if (api.GetMyArmyAround(m_target, 0).empty())
				api.Move(ai->get_action_queue(), army->pos, api.GetCityCenter(target_city));
			else
			{
				find_safe_place_around();
				return;
			}
		}

		if (target_city->hasBarrack)
			api.Recruit(ai->get_action_queue(), target_city->totalSoldier);

		bool hasRanges = false;
		bool hasCloses = false;
		bool hasEntered = false;
		vector<Army*> op_ranger_troop;
		int their_ranger_count = 0;

		for (int i = 0; i < (int) op_troop.size(); ++i) 
		{
			if (api.GetCity(op_troop[i]->pos) == api.GetCity(m_target))
				hasEntered = true;

			if (op_troop[i]->soldierType == SOLDIER_RANGER)
				op_ranger_troop.push_back(op_troop[i]);
		}

		their_ranger_count = api.GetArmyCount(op_ranger_troop);

		if (their_ranger_count == 0)
		{
			if (target_city->HP == 0 && hasEntered)
			{
				enter_state(m_name, this, new WithdrawState(m_name));
				return;
			}

			if (target_city->HP > 0)
			{
				Army *nearest = api.GetNearestArmy(army->pos, op_troop);
				if (nearest && api.HamDistance(nearest->pos, army->pos) <= SOLDIER_ATTACK_RANGE[army->soldierType])
				{
					api.AttackArmy(ai->get_action_queue(), army->pos, nearest);
					return;
				}
			}

			api.RepairCity(ai->get_action_queue(), target_city);
			return;
		} else
		{
			if (their_ranger_count > army->numSoldier)
			{
				LOG << "Strong enemy with rangers, so let's get out of this place." << endl;
				enter_state(m_name, this, new WithdrawState(m_name));
				return;
			} 
				
			if (target_city->HP == 0)
			{
				LOG << "City broken, and enemy has rangers, so let's get out of this place." << endl;
				enter_state(m_name, this, new WithdrawState(m_name));
				return;
			} 

			//	if I can attack ranger, then do it.
			Army *nearest = api.GetNearestArmy(army->pos, op_ranger_troop);
			if (nearest && api.HamDistance(nearest->pos, army->pos) <= SOLDIER_ATTACK_RANGE[army->soldierType])
			{
				api.AttackArmy(ai->get_action_queue(), army->pos, nearest);
				return;
			}

			//	if I can attack someone, then do it.
			nearest = api.GetNearestArmy(army->pos, op_troop);
			if (nearest && api.HamDistance(nearest->pos, army->pos) <= SOLDIER_ATTACK_RANGE[army->soldierType])
			{
				api.AttackArmy(ai->get_action_queue(), army->pos, nearest);
				return;
			}

			api.RepairCity(ai->get_action_queue(), target_city);
			return;
		}
	} else
	{
		vector<Army*> enemy_around = api.GetOpArmyAround(army->pos);
		int count = api.GetArmyCount(enemy_around);

		if (count > army->numSoldier)
		{
			enter_state(m_name, this, new WithdrawState(m_name));
			return;
		}
		
		api.MoveToRegion(ai->get_action_queue(), army->pos, api.GetCityCenter(target_city));
		return;
	}
}

void DefensiveBattleState::find_safe_place_around()
{
	BasicAI *ai = BasicAI::instance();
	UserAPI &api = ai->get_api();
	Army *army = api.GetCntArmy();
	LandCity *target_city = api.GetCity(m_target);
	Position center = api.GetCityCenter(target_city);

	Army *nearest = api.GetNearestArmy(army->pos, api.op_army);
	if (nearest && api.HamDistance(nearest->pos, army->pos) <= SOLDIER_ATTACK_RANGE[army->soldierType])
	{
		//	try to give a quick attack
		api.AttackArmy(ai->get_action_queue(), army->pos, nearest);
	}

	//	TODO	use road dist rather than HamDist

	int width = api.game->map->sizeX, height = api.game->map->sizeY;
	Position best(-1, -1);
	int min = INFINITY;
	for (int i = 0; i < width; i ++)
		for (int j = 0; j < height; j ++) {
			Position t(i, j);
			if (api.HamDistance(t, center) > 9) continue;
			if (!api.CheckPosition(t)) continue;
			Army *enemy = api.GetNearestArmy(t, api.op_army);
			if (enemy && api.HamDistance(t, enemy->pos) <= 9) continue;

			if (api.HamDistance(t, army->pos) < min)
			{
				min = api.HamDistance(t, army->pos);
				best = t;
			}
		}


	if (min < INFINITY)
		api.Move(ai->get_action_queue(), army->pos, best);
	else
	{
		//	how can it be like this?
		api.Move(ai->get_action_queue(), army->pos, m_target);
	}
}